package cfca.paperless.client.servlet._2_extend._2_04_SignAutoPdfProofHash;

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import cfca.com.itextpdf.text.pdf.PdfArray;
import cfca.com.itextpdf.text.pdf.PdfReader;
import cfca.com.itextpdf.text.pdf.security.PdfPKCS7;
import cfca.paperless.client.bean.MultiDataBean;
import cfca.paperless.client.bean.SealUserInfo;
import cfca.paperless.client.bean.SignLocation;
import cfca.paperless.client.servlet.PaperlessClient;
import cfca.paperless.client.servlet.PaperlessConfig;
import cfca.paperless.client.util.GUID;
import cfca.paperless.client.util.IoUtil;
import cfca.paperless.client.util.PaperlessUtil;
import cfca.paperless.client.util.PdfUtil;
import cfca.paperless.client.util.ReservedPdfPKCS7Util;
import cfca.paperless.client.util.StringUtil;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.util.Base64;
import cfca.sadk.util.HashUtil;
import cfca.sadk.x509.certificate.X509Cert;
import cfca.seal.sadk.DonePdfSeal;
import cfca.seal.sadk.PdfSealVerifier;
import cfca.seal.sadk.PrePdfSealExtra;
import cfca.seal.sadk.security.deferred.ReservedPdfPKCS7;

public class SignAutoPdfProofHashTest02 {

    /**
     * Pdf证据hash签名
     * 
     * @param args
     * @throws IOException
     * @throws
     */
    public static void main(String[] args) {
        try {
            // **********本地计算hash，对pdf进行复合计算hash，多媒体和证据hash会被放到pdf附件中
            
            long timeStart = System.currentTimeMillis();// 开始时间

            
            String filePath = "./TestData/autoCalculatePdfHash.pdf";
            byte[] pdf = FileUtils.readFileToByteArray(new File(filePath));

            
            // 如下为本地合成的多媒体信息
            byte[] handwritingImage = FileUtils.readFileToByteArray(new File("./TestData/zhangfei.png"));
            byte[] handwritingData = handwritingImage;
            byte[] identificationFrontData = FileUtils.readFileToByteArray(new File("./TestData/1.png"));
            byte[] identificationBackData = FileUtils.readFileToByteArray(new File("./TestData/2.png"));

            List<MultiDataBean> list = new ArrayList<MultiDataBean>();
            list.add(new MultiDataBean(handwritingImage, "handwritingImage.png", "2"));// 手写签名图片
            list.add(new MultiDataBean(handwritingData, "handwritingData.dat", "media-id", "0"));// 手写签名数据
            list.add(new MultiDataBean(identificationFrontData, "identificationFrontImage.png", "2"));// 身份证正面
            list.add(new MultiDataBean(identificationBackData, "identificationBackImage.png", "2"));// 身份证反面

            String[] proofHashAndMultiData = PaperlessUtil.getProofHashAndMultiData(list);

            String proofHashXml = proofHashAndMultiData[0];
            String pdfContentHash = PdfUtil.calculatePdfContentHash(pdf, new Mechanism(Mechanism.SHA1));
            proofHashXml = addProofToXml(proofHashXml, "pdfHash.pdf", pdfContentHash);
            // 合成proofHash
            pdf = PdfUtil.compoundPdfDataByAttachment(pdf, proofHashXml.getBytes(), "proofHash.xml");
            // 合成多媒体文件
            for (MultiDataBean multiDataBean : list) {
                pdf = PdfUtil.compoundPdfDataByAttachment(pdf, multiDataBean.getMultiData(), multiDataBean.getFileName());
            }

            String outputFilePath22 = "./TestData/output/3-local-compoundAutoCalculatePdfHash-" + GUID.generateId() + ".pdf";
            IoUtil.write(outputFilePath22, pdf);

            String hashAlogrithmArg = "SHA1";

            SignLocation signLocation = new SignLocation("代理人签名", "R", 10, 0);// 签名位置

            ReservedPdfPKCS7 reservedPdfPKCS7 = new ReservedPdfPKCS7();// 创建临时数据保存对象
            // 计算保留值
            String timestampServer[] = new String[] { "http://127.0.0.1:8780/Proxy/Timestamp", "", "" };

            PrePdfSealExtra prePdfSealExtra = new PrePdfSealExtra("签合同", "北京");

            // 签章时也可以使用新指定的图片，如：handwritingImage
            byte[] reservedPdf = ReservedPdfPKCS7Util.createReservedPdf(pdf, handwritingImage, 0.75f, null, hashAlogrithmArg, signLocation, reservedPdfPKCS7,
                    timestampServer, prePdfSealExtra);
            // 如下数据需要保存到数据库
            String reservedData = ReservedPdfPKCS7Util.createReservedVar(reservedPdfPKCS7);
            
            //byte[] pdfHash = reservedPdfPKCS7.hashPdf;
            byte[] pdfSealSource = reservedPdfPKCS7.signatureAttr;
            // 计算hash
            byte[] pdfSealHash = HashUtil.RSAHashMessageByBC(reservedPdfPKCS7.signatureAttr, new Mechanism(Mechanism.SHA1), false);

            long timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time--计算hash:" + (timeEnd - timeStart));


//*******************************************************************************************
        String reservedPdfBase64String = Base64.toBase64String(reservedPdf);
        //String pdfHashBase64String = Base64.toBase64String(pdfHash);
        String pdfSealSourceBase64String = Base64.toBase64String(pdfSealSource);
        //String pdfSealHashBase64String = Base64.toBase64String(pdfSealHash);

        System.out.println("reservedPdf="+reservedPdfBase64String);
        System.out.println("reservedData="+reservedData);
        //System.out.println("pdfHash="+pdfHashBase64String);
        System.out.println("pdfSealSource="+pdfSealSourceBase64String);
        //System.out.println("pdfSealHash="+pdfSealHashBase64String);
//*******************************************************************************************

            
            

            // **********调用PDF自动化Hash签名接口(带证据hash)
            
            
            timeStart = System.currentTimeMillis();// 开始时间

            //String pdfSealHashBase64String = Base64.toBase64String(pdfSealHash);

            String pdfHashListXml = "<List><PdfHash><PdfSealHash></PdfSealHash><PdfSealSource>" + pdfSealSourceBase64String + "</PdfSealSource><BizTypeCode>"
                    + "" + "</BizTypeCode></PdfHash></List>";

            SealUserInfo sealUserInfo = new SealUserInfo("张三a", "Z", "0000000000000000099", "", "", "");
            sealUserInfo.setKeyAlg("RSA");
            String userInfoXml = PaperlessUtil.buildUserInfo(sealUserInfo);

            // 操作员编码或者机构号
            String operatorCode = PaperlessConfig.operatorCode;

            // 渠道编码，可以为空
            String channelCode = PaperlessConfig.channelCode;

            PaperlessClient clientBean = new PaperlessClient(PaperlessConfig.extUrl, 10000, 60000);// 无纸化辅助接口的访问URL

            // 对pdf hash签名
            String signMessage = clientBean.signAutoPdfProofHash(pdfHashListXml, userInfoXml, proofHashXml, "", operatorCode, channelCode);
            String code = StringUtil.getNodeText(signMessage, "Code");

            if (!"200".equals(code)) {
                System.out.println("NG:signAutoPdfProofHash");
                System.out.println(signMessage);

                timeEnd = System.currentTimeMillis();// 结束时间
                System.out.println("time--signAutoPdfProofHash:" + (timeEnd - timeStart));

                return;
            }

            System.out.println("OK:autoCalculatePdfHash");
            //String pkcs1 = StringUtil.getNodeText(signMessage, "Pkcs1");
            //System.out.println("返回的pkcs1：" + pkcs1);
            String pkcs7 = StringUtil.getNodeText(signMessage, "Pkcs7");
            System.out.println("返回的pkcs7：" + pkcs7);

            


            // **********本地合成签名
            
            
            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time:" + (timeEnd - timeStart));

            // 恢复reservedPdfPKCS7对象
            reservedPdfPKCS7 = ReservedPdfPKCS7Util.initReservedVar(reservedData);
            //reservedPdfPKCS7.signaturePKCS1 = Base64.decode(pkcs1);

            // 合成外部签名
            DonePdfSeal donePdfSeal = new DonePdfSeal();
            //byte[] sealedPdfData = donePdfSeal.mergeReservedPdfSeal(new PdfReader(reservedPdf), reservedPdfPKCS7);
            byte[] sealedPdfData = donePdfSeal.mergeReservedPdfSeal(new PdfReader(reservedPdf), reservedPdfPKCS7.fieldName, Base64.decode(pkcs7));

            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time--合成外部签名:" + (timeEnd - timeStart));

            



            // **********验证签章后的PDF文件
            
            
            timeStart = System.currentTimeMillis();// 开始时间

            // 验证 pdf签章
            PdfSealVerifier pdfSealVerifier = new PdfSealVerifier(sealedPdfData);
            if (!pdfSealVerifier.isPKCS7Valid()) {
                System.out.println("NG:pdfSealVerifier error");

                timeEnd = System.currentTimeMillis();// 结束时间
                System.out.println("time--验证 pdf签章:" + (timeEnd - timeStart));

                return;
            } else {
                PdfArray uncoverdRange = pdfSealVerifier.getUncoveredByteRange();
                long uncoverdRangeLen = uncoverdRange.asLongArray()[1];
                if (uncoverdRangeLen > 0) {
                    System.out.println("NG:单据在盖章之后被修改");

                    timeEnd = System.currentTimeMillis();// 结束时间
                    System.out.println("time--验证 pdf签章:" + (timeEnd - timeStart));

                    return;
                }
            }
            
            // 验证后获取签章时间和证书主题
            Map<String, X509Cert> sigNameCertMap = pdfSealVerifier.getSigNameCertMap();
            Map<String, PdfPKCS7> sigNamePKCS7Map = pdfSealVerifier.getSigNamePKCS7Map();
            List<X509Cert> x509CertList = new ArrayList<X509Cert>(sigNameCertMap.values());
            List<PdfPKCS7> pdfPKCS7List = new ArrayList<PdfPKCS7>(sigNamePKCS7Map.values());
            
            Calendar calendar = pdfPKCS7List.get(0).getSignDate();
            Date date = calendar.getTime();

            System.out.println("签章证书主题:" + x509CertList.get(0).getSubject());
            System.out.println("签章证书序列号:" + x509CertList.get(0).getStringSerialNumber());
            System.out.println("签章时间：" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));

            String outputFilePath = "./TestData/output/signAutoPdfProofHash-" + GUID.generateId() + ".pdf";
            IoUtil.write(outputFilePath, sealedPdfData);
            System.out.println("pdfSealVerifier ok " + outputFilePath);

            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time--验证 pdf签章:" + (timeEnd - timeStart));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static String addProofToXml(String xmlContent, String fileName, String hash) throws DocumentException {
        Document document = DocumentHelper.parseText(xmlContent);
        Element root = document.getRootElement();
        Element newElement = root.addElement("Proof");
        newElement.addAttribute("fileName", fileName);
        newElement.addAttribute("hash", hash);
        return document.asXML();
    }

}
